/******************************************************
*  Copyright(c) pages.ilovezxl.top
*  FileName :ringbuf.c
*  Author : rivulet.dave
*  Description : »·ÐÎ»º´æ²Ù×÷
*  Data : 2019-04-15
******************************************************/

/*********************************************************************
 * INCLUDES
 */

#include "stdrope.h"

#include "sallib.h"
#include "mem.h"
#include "ringbuf.h"

/*********************************************************************
 *  EXTERNAL VARIABLES
 */

/*********************************************************************
 * MACROS
 */

#define ELEMENT_ADDR(ringbuf, n) (ringbuf->pBuffer + (n)*(ringbuf->usElementSize))

/*********************************************************************
 * TYPES
 */


/*********************************************************************
 * FUNCTIONS DECLARE
 */

static uint16_t RB_RingBufSpace(RING_BUF_S *pstRingBuf);

/*********************************************************************
 *  VARIABLES
 */

/*********************************************************************
 * FUNCTIONS
 */

/**
 *   @Description: »·ÐÎ»º´æ³õÊ¼»¯
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @param: usBufSize - Èë²Î£¬»º´æ´óÐ¡
 *   @param: usElementSize - Èë²Î£¬»º´æ±£´æµÄÔªËØ´óÐ¡
 *   @return £º´íÎóÐÅÏ¢
 */
int32_t RB_RingBufInit(RING_BUF_S *pstRingBuf, uint16_t usBufSize, uint16_t usElementSize)
{
    osMutexId_t pMutexID;
    
    if (NULL == pstRingBuf)
    {
        return _ERR;
    }

    // ÒÑ³õÊ¼»¯
    if (NULL != pstRingBuf->pBuffer)
    {
        return _ERR;
    }

    pMutexID = osMutexNew(NULL);
    if (NULL == pMutexID)
    {
        return _ERR;
    }

    pstRingBuf->pMutexID = pMutexID;
    pstRingBuf->usRPos = 0;
    pstRingBuf->usWPos = 0;
    pstRingBuf->usElementSize = usElementSize;
    pstRingBuf->usBufSize = usBufSize;
    pstRingBuf->pBuffer = osMemAlloc(usBufSize * usElementSize);
    if (NULL == pstRingBuf->pBuffer)
    {
        return _ERR;
    }

    return _OK;
}

/**
 *   @Description: »·ÐÎ»º´æÏû»Ù
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @return £º´íÎóÐÅÏ¢
 */
int32_t RB_RingBufDestroy(RING_BUF_S *pstRingBuf)
{
    if (NULL == pstRingBuf)
    {
        return _ERR;
    }

    // ÊÍ·ÅÄÚ´æ
    osMemFree(&pstRingBuf->pBuffer);
    pstRingBuf->usElementSize = 0;
    pstRingBuf->usBufSize = 0;

    // ÊÍ·Åmutex
    osMutexDelete(pstRingBuf->pMutexID);
    
    return _OK;
}

/**
 *   @Description: »·ÐÎ»º´æÖØÖÃ£¬²»ÇåÄÚ´æ£¬¶ªÆú»º´æÊý¾Ý
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @return £º´íÎóÐÅÏ¢
 */
int32_t RB_RingBufReset(RING_BUF_S *pstRingBuf)
{
    if (NULL == pstRingBuf)
    {
        return _ERR;
    }
    
    pstRingBuf->usRPos = 0;
    pstRingBuf->usWPos = 0;
    
    return _OK;
}

/**
 *   @Description: »·ÐÎ»º´æÊÇ·ñÎª¿Õ
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @return £º_TRUE/_FALSE
 */
bool RB_RingBufIsEmpty(RING_BUF_S *pstRingBuf)
{
    if (NULL == pstRingBuf)
    {
        return true;
    }

    if (!pstRingBuf->usBufSize)
    {
        return true;
    }

    if (pstRingBuf->usBufSize - 1 != RB_RingBufSpace(pstRingBuf))
    {
        return false;
    }
    
    return true;
}

/**
 *   @Description: »·ÐÎ»º´æÊÇ·ñÒÑÂú
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @return £º_TRUE/_FALSE
 */
bool RB_RingBufIsFull(RING_BUF_S *pstRingBuf)
{
    if (0 == RB_RingBufSpace(pstRingBuf))
    {
        return true;
    }
    
    return false;
}

/**
 *   @Description: ¶Á»·ÐÎ»º´æÊý¾Ý
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @param: pData - ³ö²Î£¬¶Á³öÀ´µÄÊý¾Ý
 *   @return £º´íÎóÐÅÏ¢
 */
int32_t RB_RingBufPop(RING_BUF_S *pstRingBuf, void *pData)
{
    if (pstRingBuf == NULL || pData == NULL)
    {
        return _ERR;
    }
    
    if (RB_RingBufIsEmpty(pstRingBuf))
    {
        return _ERR;
    }

    osMutexAcquire(pstRingBuf->pMutexID, 0);
    memcpy(pData, ELEMENT_ADDR(pstRingBuf, pstRingBuf->usRPos), pstRingBuf->usElementSize);
    if (1 + pstRingBuf->usRPos >= pstRingBuf->usBufSize)
    {
        pstRingBuf->usRPos = 0;
    }
    else
    {
        pstRingBuf->usRPos++;
    }
    osMutexRelease(pstRingBuf->pMutexID);

    return _OK;
}

/**
 *   @Description: Ð´»·ÐÎ»º´æÊý¾Ý
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @param: pData - Èë²Î£¬Òª±£´æµÄÊý¾Ý
 *   @return £º´íÎóÐÅÏ¢
 */
int32_t RB_RingBufPush(RING_BUF_S *pstRingBuf, const void *pData)
{
    if (RB_RingBufIsFull(pstRingBuf))
    {
        // ¿É¿¼ÂÇ¸²¸Ç¾ÉµÄÊý¾Ý
        return _ERR;
    }

    osMutexAcquire(pstRingBuf->pMutexID, 0);
    memcpy(ELEMENT_ADDR(pstRingBuf, pstRingBuf->usWPos), pData, pstRingBuf->usElementSize);
    if (1 + pstRingBuf->usWPos >= pstRingBuf->usBufSize)
    {
        pstRingBuf->usWPos = 0;
    }
    else
    {
        pstRingBuf->usWPos++;
    }
    osMutexRelease(pstRingBuf->pMutexID);

    return _OK;
}

/**
 *   @Description: »·ÐÎ»º´æÊ£Óà¿Õ¼ä
 *   @param: pstRingBuf - Èë²Î£¬»º´æÍ·Ö¸Õë
 *   @return £º»º´æÊ£Óà¿Õ¼ä¸öÊý
 */
static uint16_t RB_RingBufSpace(RING_BUF_S *pstRingBuf)
{
    uint16_t usRPos;
    
    if (pstRingBuf == NULL)
    {
        return 0;
    }
    
    // when use space , we want fill in , @usRPos may change. so
    usRPos =  pstRingBuf->usRPos;
    if (pstRingBuf->usWPos < usRPos)
    {
        return usRPos - pstRingBuf->usWPos -1;
    }
    
    return pstRingBuf->usBufSize - 1 - pstRingBuf->usWPos + usRPos;
}

